<?php

namespace App\Http\Service;

// 聊天服务层 用来写聊天存储等有关逻辑
use App\Utils\BaiDuVoice;
use App\Utils\MyRedis;
use Illuminate\Support\Facades\Validator;

class IMService
{
    private $iMConversationList = [
        1 => '短语音生成工具'
    ];

    // MyRedis对象
    private $myRedis;
    // redis对象
    private $redis;
    // 过期天数 10天
    private $expireDay = 10;


    public function __construct()
    {
        // 获取redis对象
        $this->myRedis = MyRedis::getInstance();
        $this->redis = $this->myRedis->getRedis();
    }


    // 从redis中获取 redis中数据只保留10天
    // 数据需要包含 名称 消息列表 消息条数 消息内容 时间/类型/数据/发送者
    // redis中 存放原则 zset: userid:会话id:时间到微秒  前端也限制一下消息长度
    // 拟定 短语音生成工具会话id为1

    //用zset存放 zset整体设置过期时间 初始化的时候重制过期时间
    //zset中的数据进行遍历 里面写一个逻辑过期时间
    //如果逻辑过期了 就从zset中删除这条数据
    //im:userid:1:

    /**
     * 获取处理后的数据
     * @param $uid
     * @return array
     */
    public function getImHistoryListDealList($uid)
    {
        try {
            $data = $this->getImHistoryList(intval($uid));
            foreach ($data as $key => $dl) {
                //生成一下日期的提示 如果时间改变了就生成一条时间数据
                $now_date = '';
                //新的数据数组
                $new_list = [];
                foreach ($dl['list'] as $d) {
                    if ($now_date !== $d['date']) {
                        $new_list[] = $this->createDateTip($d['date']);
                        $now_date = $d['date'];
                    }
                    $new_list[] = $d;
                }
                $data[$key]['list'] = $new_list;
            }
            return create_response_data('成功', true, $data);
        } catch (\Exception $e) {
            return create_response_data('获取历史消息失败', false, [
                'err' => $e->getMessage()
            ]);
        }
    }

    /**
     * 根据用户编号获取历史消息
     * @param int $uid 用户编号
     * @return array 历史消息数组 ['name'=>'会话名称','list'=>'消息列表']
     * @throws \RedisException
     */
    public function getImHistoryList(int $uid): array
    {
        // 获取全部键值对数据
        $prefix = "im:$uid:";
        $keys = $this->redis->keys($prefix . '*');
        // 获取对应的数组
        $historyList = [];
        foreach ($keys as $key) {
            // 获取zset中的全部数据 升序排序
            $list = $this->redis->zRange($key, 0, -1);
            // 处理过期数据
            $list = $this->iMListExpire($key, $list);
            // 获取对话描述的key
            $conKey = $this->myRedis->getKeyInfoByIndex($key, 2);
            $historyList[] = [
                'name' => $this->iMConversationList[$conKey],
                'list' => $list
            ];
        }
        return $historyList;
    }

    /**
     * 处理逻辑过期数据 并且返回新的数组
     * @param string $key 需要处理的redis key值
     * @param array $list redis数据 string未转json
     * @return array 新的数组 json数据
     * @throws \RedisException
     */
    public function iMListExpire(string $key, array $list): array
    {
        $now_time = time();
        $del_arr = [];
        $new_list = [];
        foreach ($list as $value) {
            $data = json_decode($value, true);
            if ($now_time > $data['expire']) {
                // 数据过期 存入需要删除的数组中
                $del_arr[] = $value;
            } else {
                $new_list[] = $data;
            }
        }
        //批量删除减少redis操作次数
        if (count($del_arr)) {
            $this->redis->zRem($key, ...$del_arr);
        }
        return $new_list;
    }

    /**
     * 发送消息方法 暂时只有百度语音
     * @param int $uid user_id
     * @param string $message 消息
     * @return array
     */
    public function sendMessage(int $uid, string $message): array
    {
        # 验证数据
        $validator = Validator::make(
            [
                'message' => $message
            ],
            [
                'message' => ['required', 'regex:/^[\x7f-\xff]+$/'],
            ],
            [
                'message.required' => '请填写需要发送的信息',
                'message.regex' => '消息需要为全中文',
            ],
        );
        # 验证失败直接返回
        if ($validator->fails()) {
            $errors = $validator->errors();
            return create_response_data($errors->first(), false);
        }
        # 验证成功
        # 1记录redis数据
        $message_data = $this->setImInfoRedis($uid, 1, $message);
        if (!$message_data) {
            return create_response_data('写入消息数据失败', false);
        }
        # 2生成百度语音数据
        $voice = new BaiDuVoice();
        $data = $voice->tts($message);
        if ($data['code'] !== 200) {
            return $data;
        }
        # 3将百度语音数据记录到redis
        $voice_data = $this->setImInfoRedis($uid, 1, $data['data']['url'], 'voice', 'sys');
        if (!$voice_data) {
            return create_response_data('写入语音数据失败', false);
        }
        # 返回百度语音数据
        return create_response_data('成功', true, ['message_data' => $message_data, 'voice_data' => $voice_data]);
    }

    /**
     * 写入redis数据
     * @param int $uid 用户编号
     * @param int $conKey 服务key
     * @param string $message 消息
     * @param string $type 消息类型 normal warning voice
     * @param string $source 消息来源 user sys tip
     * @return array|false
     * @throws \RedisException
     */
    public function setImInfoRedis(int $uid, int $conKey, string $message, string $type = 'normal', string $source = 'user')
    {
        $key = "im:$uid:$conKey";
        $m_time = $this->createImMicroTime();
        $time = time();
        $uuid = create_uuid();
        $data = [
            // 消息
            'message' => $message,
            // 时间
            'time' => date('H:i:s', $time),
            'date' => date('Y-m-d', $time),
            // 类型
            'type' => $type,
            // 消息源 user或者sys
            'source' => $source,
            // 过期时间
            'expire' => time() + $this->expireDay * 24 * 60 * 60,
            // 避免消息重复 加上一个uuid
            'uuid' => $uuid
        ];
        $data_str = json_encode($data);
        // redis中设置数据
        $insert = $this->redis->zAdd($key, $m_time, $data_str);
        // 更新key ttl为xx天之后删除
        $this->redis->expire($key, $this->expireDay * 24 * 60 * 60);
        if (!$insert) {
            return false;
        } else {
            return $data;
        }
    }

    /**
     * 生成微秒级的时间数据用于存放IM数据
     * @return string
     */
    public function createImMicroTime(): string
    {
        $micro_time = microtime();
        $micro_time_ex = explode(" ", $micro_time);
        //生成存放聊天数据的时间 使用number_fromat生成非科学计数法的数字
        return number_format(($micro_time_ex[1] * (10 ** 8) + $micro_time_ex[0] * (10 ** 8)), 0, '', '');
    }


    /**
     * 生成日期提示数据
     * @param string $date 日期
     * @return array
     */
    public function createDateTip(string $date): array
    {
        return [
            // 消息
            'message' => $date,
            // 时间
            'time' => '',
            'date' => $date,
            // 类型
            'type' => 'normal',
            // 消息源 user或者sys或者tip
            'source' => 'tip',
            // 过期时间
            'expire' => time() + $this->expireDay * 24 * 60 * 60,
            // 避免消息重复 加上一个uuid
            'uuid' => create_uuid()
        ];
    }
}
